/*
 * dlfcn-win32
 * Copyright (c) 2007 Ramiro Polla
 * Copyright (c) 2015 Tiancheng "Timothy" Gu
 * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
 *
 * dlfcn-win32 is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * dlfcn-win32 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with dlfcn-win32; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifdef _WIN32

#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif // _DEBUG
#pragma warning(push)
#pragma warning(disable: 5105)
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#pragma warning(pop)

 /* Older versions do not have this type */
#if _WIN32_WINNT < 0x0500
typedef ULONG ULONG_PTR;
#endif

/* Older SDK versions do not have these macros */
#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4
#endif
#ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2
#endif

#ifdef _MSC_VER
/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
#pragma intrinsic( _ReturnAddress )
#else
/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
#ifndef _ReturnAddress
#define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) )
#endif
#endif

#ifdef DLFCN_WIN32_SHARED
#define DLFCN_WIN32_EXPORTS
#endif
#include "sys/dlfcn_windows.h"

#if defined( _MSC_VER ) && _MSC_VER >= 1300
/* https://docs.microsoft.com/en-us/cpp/cpp/noinline */
#define DLFCN_NOINLINE __declspec( noinline )
#elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) )
/* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */
#define DLFCN_NOINLINE __attribute__(( noinline ))
#else
#define DLFCN_NOINLINE
#endif

/* Note:
 * MSDN says these functions are not thread-safe. We make no efforts to have
 * any kind of thread safety.
 */

typedef struct local_object {
    HMODULE hModule;
    struct local_object* previous;
    struct local_object* next;
} local_object;

static local_object first_object;

/* These functions implement a double linked list for the local objects. */
static local_object* local_search(HMODULE hModule)
{
    local_object* pobject;

    if (hModule == NULL)
        return NULL;

    for (pobject = &first_object; pobject; pobject = pobject->next)
        if (pobject->hModule == hModule)
            return pobject;

    return NULL;
}

static BOOL local_add(HMODULE hModule)
{
    local_object* pobject;
    local_object* nobject;

    if (hModule == NULL)
        return TRUE;

    pobject = local_search(hModule);

    /* Do not add object again if it's already on the list */
    if (pobject != NULL)
        return TRUE;

    for (pobject = &first_object; pobject->next; pobject = pobject->next);

    nobject = (local_object*)malloc(sizeof(local_object));

    if (!nobject)
        return FALSE;

    pobject->next = nobject;
    nobject->next = NULL;
    nobject->previous = pobject;
    nobject->hModule = hModule;

    return TRUE;
}

static void local_rem(HMODULE hModule)
{
    local_object* pobject;

    if (hModule == NULL)
        return;

    pobject = local_search(hModule);

    if (pobject == NULL)
        return;

    if (pobject->next)
        pobject->next->previous = pobject->previous;
    if (pobject->previous)
        pobject->previous->next = pobject->next;

    free(pobject);
}

/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
 * static buffer.
 * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
 * the limit.
 * EDIT 20210608: change buffer smaller.
 */
static char error_buffer[512 * 1024];
static BOOL error_occurred;

static void save_err_str(const char* str, DWORD dwMessageId)
{
    DWORD ret;
    size_t pos, len;

    len = strlen(str);
    if (len > sizeof(error_buffer) - 5)
        len = sizeof(error_buffer) - 5;

    /* Format error message to:
     * "<argument to function that failed>": <Windows localized error message>
      */
    pos = 0;
    error_buffer[pos++] = '"';
    memcpy(error_buffer + pos, str, len);
    pos += len;
    error_buffer[pos++] = '"';
    error_buffer[pos++] = ':';
    error_buffer[pos++] = ' ';

    ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        error_buffer + pos, (DWORD)(sizeof(error_buffer) - pos), NULL);
    pos += ret;

    /* When FormatMessageA() fails it returns zero and does not touch buffer
     * so add trailing null byte */
    if (ret == 0)
        error_buffer[pos] = '\0';

    if (pos > 1)
    {
        /* POSIX says the string must not have trailing <newline> */
        if (error_buffer[pos - 2] == '\r' && error_buffer[pos - 1] == '\n')
            error_buffer[pos - 2] = '\0';
    }

    error_occurred = TRUE;
}

static void save_err_ptr_str(const void* ptr, DWORD dwMessageId)
{
    char ptr_buf[2 + 2 * sizeof(ptr) + 1];
    char num;
    size_t i;

    ptr_buf[0] = '0';
    ptr_buf[1] = 'x';

    for (i = 0; i < 2 * sizeof(ptr); i++)
    {
        num = (char)((((ULONG_PTR)ptr) >> (8 * sizeof(ptr) - 4 * (i + 1))) & 0xF);
        ptr_buf[2 + i] = num + ((num < 0xA) ? '0' : ('A' - 0xA));
    }

    ptr_buf[2 + 2 * sizeof(ptr)] = 0;

    save_err_str(ptr_buf, dwMessageId);
}

static HMODULE MyGetModuleHandleFromAddress(const void* addr)
{
    static BOOL(WINAPI * GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE*) = NULL;
    static BOOL failed = FALSE;
    HMODULE kernel32;
    HMODULE hModule;
    MEMORY_BASIC_INFORMATION info;
    SIZE_T sLen;

    if (!failed && GetModuleHandleExAPtr == NULL)
    {
        kernel32 = GetModuleHandleA("Kernel32.dll");
        if (kernel32 != NULL)
            GetModuleHandleExAPtr = (BOOL(WINAPI*)(DWORD, LPCSTR, HMODULE*)) GetProcAddress(kernel32, "GetModuleHandleExA");
        if (GetModuleHandleExAPtr == NULL)
            failed = TRUE;
    }

    if (!failed)
    {
        /* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */
        if (!GetModuleHandleExAPtr(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule))
            return NULL;
    }
    else
    {
        /* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380
         * The HMODULE of a DLL is the same value as the module's base address.
         */
        sLen = VirtualQuery(addr, &info, sizeof(info));
        if (sLen != sizeof(info))
            return NULL;
        hModule = (HMODULE)info.AllocationBase;
    }

    return hModule;
}

/* Load Psapi.dll at runtime, this avoids linking caveat */
static BOOL MyEnumProcessModules(HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded)
{
    static BOOL(WINAPI * EnumProcessModulesPtr)(HANDLE, HMODULE*, DWORD, LPDWORD) = NULL;
    static BOOL failed = FALSE;
    UINT uMode;
    HMODULE psapi;

    if (failed)
        return FALSE;

    if (EnumProcessModulesPtr == NULL)
    {
        /* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */
        psapi = GetModuleHandleA("Kernel32.dll");
        if (psapi != NULL)
            EnumProcessModulesPtr = (BOOL(WINAPI*)(HANDLE, HMODULE*, DWORD, LPDWORD)) GetProcAddress(psapi, "K32EnumProcessModules");

        /* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */
        if (EnumProcessModulesPtr == NULL)
        {
            /* Do not let Windows display the critical-error-handler message box */
            uMode = SetErrorMode(SEM_FAILCRITICALERRORS);
            psapi = LoadLibraryA("Psapi.dll");
            if (psapi != NULL)
            {
                EnumProcessModulesPtr = (BOOL(WINAPI*)(HANDLE, HMODULE*, DWORD, LPDWORD)) GetProcAddress(psapi, "EnumProcessModules");
                if (EnumProcessModulesPtr == NULL)
                    FreeLibrary(psapi);
            }
            SetErrorMode(uMode);
        }

        if (EnumProcessModulesPtr == NULL)
        {
            failed = TRUE;
            return FALSE;
        }
    }

    return EnumProcessModulesPtr(hProcess, lphModule, cb, lpcbNeeded);
}

DLFCN_EXPORT
void* dlopen(const char* file, int mode)
{
    HMODULE hModule;
    UINT uMode;

    error_occurred = FALSE;

    /* Do not let Windows display the critical-error-handler message box */
    uMode = SetErrorMode(SEM_FAILCRITICALERRORS);

    if (file == NULL)
    {
        /* POSIX says that if the value of file is NULL, a handle on a global
         * symbol object must be provided. That object must be able to access
         * all symbols from the original program file, and any objects loaded
         * with the RTLD_GLOBAL flag.
         * The return value from GetModuleHandle( ) allows us to retrieve
         * symbols only from the original program file. EnumProcessModules() is
         * used to access symbols from other libraries. For objects loaded
         * with the RTLD_LOCAL flag, we create our own list later on. They are
         * excluded from EnumProcessModules() iteration.
         */
        hModule = GetModuleHandle(NULL);

        if (!hModule)
            save_err_str("(null)", GetLastError());
    }
    else
    {
        HANDLE hCurrentProc;
        DWORD dwProcModsBefore, dwProcModsAfter;
        char lpFileName[MAX_PATH];
        size_t i, len;

        len = strlen(file);

        if (len >= sizeof(lpFileName))
        {
            save_err_str(file, ERROR_FILENAME_EXCED_RANGE);
            hModule = NULL;
        }
        else
        {
            /* MSDN says backslashes *must* be used instead of forward slashes. */
            for (i = 0; i < len; i++)
            {
                if (file[i] == '/')
                    lpFileName[i] = '\\';
                else
                    lpFileName[i] = file[i];
            }
            lpFileName[len] = '\0';

            hCurrentProc = GetCurrentProcess();

            if (MyEnumProcessModules(hCurrentProc, NULL, 0, &dwProcModsBefore) == 0)
                dwProcModsBefore = 0;

            /* POSIX says the search path is implementation-defined.
             * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
             * to UNIX's search paths (start with system folders instead of current
             * folder).
             */
            hModule = LoadLibraryExA(lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);

            if (!hModule)
            {
                save_err_str(lpFileName, GetLastError());
            }
            else
            {
                if (MyEnumProcessModules(hCurrentProc, NULL, 0, &dwProcModsAfter) == 0)
                    dwProcModsAfter = 0;

                /* If the object was loaded with RTLD_LOCAL, add it to list of local
                 * objects, so that its symbols cannot be retrieved even if the handle for
                 * the original program file is passed. POSIX says that if the same
                 * file is specified in multiple invocations, and any of them are
                 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
                 * symbols will remain global. If number of loaded modules was not
                 * changed after calling LoadLibraryEx(), it means that library was
                 * already loaded.
                 */
                if ((mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter)
                {
                    if (!local_add(hModule))
                    {
                        save_err_str(lpFileName, ERROR_NOT_ENOUGH_MEMORY);
                        FreeLibrary(hModule);
                        hModule = NULL;
                    }
                }
                else if (!(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter)
                {
                    local_rem(hModule);
                }
            }
        }
    }

    /* Return to previous state of the error-mode bit flags. */
    SetErrorMode(uMode);

    return (void*)hModule;
}

DLFCN_EXPORT
int dlclose(void* handle)
{
    HMODULE hModule = (HMODULE)handle;
    BOOL ret;

    error_occurred = FALSE;

    ret = FreeLibrary(hModule);

    /* If the object was loaded with RTLD_LOCAL, remove it from list of local
     * objects.
     */
    if (ret)
        local_rem(hModule);
    else
        save_err_ptr_str(handle, GetLastError());

    /* dlclose's return value in inverted in relation to FreeLibrary's. */
    ret = !ret;

    return (int)ret;
}

DLFCN_NOINLINE /* Needed for _ReturnAddress() */
DLFCN_EXPORT
void* dlsym(void* handle, const char* name)
{
    FARPROC symbol;
    HMODULE hCaller;
    HMODULE hModule;
    DWORD dwMessageId;

    error_occurred = FALSE;

    symbol = NULL;
    hCaller = NULL;
    hModule = GetModuleHandle(NULL);
    dwMessageId = 0;

    if (handle == RTLD_DEFAULT)
    {
        /* The symbol lookup happens in the normal global scope; that is,
         * a search for a symbol using this handle would find the same
         * definition as a direct use of this symbol in the program code.
         * So use same lookup procedure as when filename is NULL.
         */
        handle = hModule;
    }
    else if (handle == RTLD_NEXT)
    {
        /* Specifies the next object after this one that defines name.
         * This one refers to the object containing the invocation of dlsym().
         * The next object is the one found upon the application of a load
         * order symbol resolution algorithm. To get caller function of dlsym()
         * use _ReturnAddress() intrinsic. To get HMODULE of caller function
         * use MyGetModuleHandleFromAddress() which calls either standard
         * GetModuleHandleExA() function or hack via VirtualQuery().
         */
        hCaller = MyGetModuleHandleFromAddress(_ReturnAddress());

        if (hCaller == NULL)
        {
            dwMessageId = ERROR_INVALID_PARAMETER;
            goto end;
        }
    }

    if (handle != RTLD_NEXT)
    {
        symbol = GetProcAddress((HMODULE)handle, name);

        if (symbol != NULL)
            goto end;
    }

    /* If the handle for the original program file is passed, also search
     * in all globally loaded objects.
     */

    if (hModule == handle || handle == RTLD_NEXT)
    {
        HANDLE hCurrentProc;
        HMODULE* modules;
        DWORD cbNeeded;
        DWORD dwSize;
        size_t i;

        hCurrentProc = GetCurrentProcess();

        /* GetModuleHandle( NULL ) only returns the current program file. So
         * if we want to get ALL loaded module including those in linked DLLs,
         * we have to use EnumProcessModules( ).
         */
        if (MyEnumProcessModules(hCurrentProc, NULL, 0, &dwSize) != 0)
        {
            modules = malloc(dwSize);
            if (modules)
            {
                if (MyEnumProcessModules(hCurrentProc, modules, dwSize, &cbNeeded) != 0 && dwSize == cbNeeded)
                {
                    for (i = 0; i < dwSize / sizeof(HMODULE); i++)
                    {
                        if (handle == RTLD_NEXT && hCaller)
                        {
                            /* Next modules can be used for RTLD_NEXT */
                            if (hCaller == modules[i])
                                hCaller = NULL;
                            continue;
                        }
                        if (local_search(modules[i]))
                            continue;
                        symbol = GetProcAddress(modules[i], name);
                        if (symbol != NULL)
                        {
                            free(modules);
                            goto end;
                        }
                    }

                }
                free(modules);
            }
            else
            {
                dwMessageId = ERROR_NOT_ENOUGH_MEMORY;
                goto end;
            }
        }
}

end:
    if (symbol == NULL)
    {
        if (!dwMessageId)
            dwMessageId = ERROR_PROC_NOT_FOUND;
        save_err_str(name, dwMessageId);
    }

    return *(void**)(&symbol);
}

DLFCN_EXPORT
char* dlerror(void)
{
    /* If this is the second consecutive call to dlerror, return NULL */
    if (!error_occurred)
        return NULL;

    /* POSIX says that invoking dlerror( ) a second time, immediately following
     * a prior invocation, shall result in NULL being returned.
     */
    error_occurred = FALSE;

    return error_buffer;
}

/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2
 * for details */

 /* Get specific image section */
static BOOL get_image_section(HMODULE module, int index, void** ptr, DWORD* size)
{
    IMAGE_DOS_HEADER* dosHeader;
    IMAGE_OPTIONAL_HEADER* optionalHeader;

    dosHeader = (IMAGE_DOS_HEADER*)module;

    if (dosHeader->e_magic != 0x5A4D)
        return FALSE;

    optionalHeader = (IMAGE_OPTIONAL_HEADER*)((BYTE*)module + dosHeader->e_lfanew + 24);

    if (optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
        return FALSE;

    if (index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
        return FALSE;

    if (optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0)
        return FALSE;

    if (size != NULL)
        *size = optionalHeader->DataDirectory[index].Size;

    *ptr = (void*)((BYTE*)module + optionalHeader->DataDirectory[index].VirtualAddress);

    return TRUE;
}

/* Return symbol name for a given address from export table */
static const char* get_export_symbol_name(HMODULE module, IMAGE_EXPORT_DIRECTORY* ied, const void* addr, void** func_address)
{
    DWORD i;
    void* candidateAddr = NULL;
    int candidateIndex = -1;
    BYTE* base = (BYTE*)module;
    DWORD* functionAddressesOffsets = (DWORD*)(base + ied->AddressOfFunctions);
    DWORD* functionNamesOffsets = (DWORD*)(base + ied->AddressOfNames);
    USHORT* functionNameOrdinalsIndexes = (USHORT*)(base + ied->AddressOfNameOrdinals);

    for (i = 0; i < ied->NumberOfFunctions; i++)
    {
        if ((void*)(base + functionAddressesOffsets[i]) > addr || candidateAddr >= (void*)(base + functionAddressesOffsets[i]))
            continue;

        candidateAddr = (void*)(base + functionAddressesOffsets[i]);
        candidateIndex = i;
    }

    if (candidateIndex == -1)
        return NULL;

    *func_address = candidateAddr;

    for (i = 0; i < ied->NumberOfNames; i++)
    {
        if (functionNameOrdinalsIndexes[i] == candidateIndex)
            return (const char*)(base + functionNamesOffsets[i]);
    }

    return NULL;
}

static BOOL is_valid_address(const void* addr)
{
    MEMORY_BASIC_INFORMATION info;
    SIZE_T result;

    if (addr == NULL)
        return FALSE;

    /* check valid pointer */
    result = VirtualQuery(addr, &info, sizeof(info));

    if (result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS)
        return FALSE;

    return TRUE;
}

/* Return state if address points to an import thunk
 *
 * An import thunk is setup with a 'jmp' instruction followed by an
 * absolute address (32bit) or relative offset (64bit) pointing into
 * the import address table (iat), which is partially maintained by
 * the runtime linker.
 */
static BOOL is_import_thunk(const void* addr)
{
    return *(short*)addr == 0x25ff ? TRUE : FALSE;
}

/* Return adress from the import address table (iat),
 * if the original address points to a thunk table entry.
 */
static void* get_address_from_import_address_table(void* iat, DWORD iat_size, const void* addr)
{
    BYTE* thkp = (BYTE*)addr;
    /* Get offset from thunk table (after instruction 0xff 0x25)
     *   4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
     */
    ULONG offset = *(ULONG*)(thkp + 2);
#ifdef _WIN64
    /* On 64 bit the offset is relative
     *      4018c8:   ff 25 4a 8a 00 00    jmpq    *0x8a4a(%rip)    # 40a318 <__imp_VirtualQuery>
     * And can be also negative (MSVC in WDK)
     *   100002f20:   ff 25 3a e1 ff ff    jmpq   *-0x1ec6(%rip)    # 0x100001060
     * So cast to signed LONG type
     */
    BYTE* ptr = (BYTE*)(thkp + 6 + (LONG)offset);
#else
    /* On 32 bit the offset is absolute
     *   4019b4:    ff 25 90 71 40 00    jmp    *0x40719
     */
    BYTE* ptr = (BYTE*)offset;
#endif

    if (!is_valid_address(ptr) || ptr < (BYTE*)iat || ptr >(BYTE*) iat + iat_size)
        return NULL;

    return *(void**)ptr;
}

/* Holds module filename */
static char module_filename[2 * MAX_PATH];

static BOOL fill_info(const void* addr, Dl_info* info)
{
    HMODULE hModule;
    DWORD dwSize;
    IMAGE_EXPORT_DIRECTORY* ied;
    void* funcAddress = NULL;

    /* Get module of the specified address */
    hModule = MyGetModuleHandleFromAddress(addr);

    if (hModule == NULL)
        return FALSE;

    dwSize = GetModuleFileNameA(hModule, module_filename, sizeof(module_filename));

    if (dwSize == 0 || dwSize == sizeof(module_filename))
        return FALSE;

    info->dli_fname = module_filename;
    info->dli_fbase = (void*)hModule;

    /* Find function name and function address in module's export table */
    if (get_image_section(hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void**)&ied, NULL))
        info->dli_sname = get_export_symbol_name(hModule, ied, addr, &funcAddress);
    else
        info->dli_sname = NULL;

    info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : (void*)addr;

    return TRUE;
}

DLFCN_EXPORT
int dladdr(const void* addr, Dl_info* info)
{
    if (info == NULL)
        return 0;

    if (!is_valid_address(addr))
        return 0;

    if (is_import_thunk(addr))
    {
        void* iat;
        DWORD iatSize;
        HMODULE hModule;

        /* Get module of the import thunk address */
        hModule = MyGetModuleHandleFromAddress(addr);

        if (hModule == NULL)
            return 0;

        if (!get_image_section(hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize))
        {
            /* Fallback for cases where the iat is not defined,
             * for example i586-mingw32msvc-gcc */
            IMAGE_IMPORT_DESCRIPTOR* iid;
            DWORD iidSize;

            if (!get_image_section(hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void**)&iid, &iidSize))
                return 0;

            if (iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0)
                return 0;

            iat = (void*)((BYTE*)hModule + iid->FirstThunk);
            /* We assume that in this case iid and iat's are in linear order */
            iatSize = iidSize - (DWORD)((BYTE*)iat - (BYTE*)iid);
        }

        addr = get_address_from_import_address_table(iat, iatSize, addr);

        if (!is_valid_address(addr))
            return 0;
    }

    if (!fill_info(addr, info))
        return 0;

    return 1;
}

//#ifdef DLFCN_WIN32_SHARED
//BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
//{
//    (void)hinstDLL;
//    (void)fdwReason;
//    (void)lpvReserved;
//    return TRUE;
//}
//#endif

#endif // _WIN32
